<?php
/**
* 文件上传类
* @author moufer<moufer@163.com>
* @copyright www.modoer.com
*/
!defined('IN_MUDDER') && exit('Access Denied');

class ms_upload
{
	public static function __callStatic($name, $arguments) 
	{
		$class = 'upload_'.$name;
		if(class_exists($class)) {
			$key_name = $arguments[0];
			$options = $arguments[1] ?: array();
			return new $class($key_name, $options);
		}
		show_error(lang('global_op_function_not_exists', 'ms_upload::'.$name));
	}

	//删除一张图片（）
	public static function delete($filepath, $relative_path = true)
	{
		if(is_array($filepath)) {
			foreach($filepath as $file) self::delete($file, $relative_path);
		} else {
			if(!$filepath||strlen($filepath)<4) return false;
			$file = ($relative_path?MUDDER_ROOT:'') . $filepath;
			if(strposex($file, '../')) return false;
			if(is_dir($file)) return false;
			if(!is_file($file)) return false;
			@unlink($file);
		}
	}
}

/**
 * 上传文件
 */
class upload_file extends ms_base 
{
	public $filename='';					//文件名称
	public $path='';						//文件相对目录
	public $filepath='';					//完整的文件路径+文件名
	public $url='';						//URL相对路径
	public $filesize=0;					//文件大小
	public $prefix='';					//上传文件命名的前缀

	//private
	protected $_file;					//当前准备获取的上传文件信息
	protected $option;					//配置参数

	public function __construct($key_name, $options = array())
	{
		parent::__construct();
		//参数配置
		$this->set_options($options);
		$this->key_name = $key_name;
		//文件检测
		$this->check_file();
	}

	/**
	 * 设置参数
	 * @param array $options 参数数组
	 * key_name = 'file';		//上传文件信息数组$_FILES内准备的某组键名
	 * max_filesize = 1000000;	//文件上传大小最大值
	 * file_exts = array();		//允许上传的文件格式（文件名后缀）
	 * file_dir_mod = 'MONTH';	//文件存放的方式（array('MONTH'=>'月','WEEK'=>'周','DAY'=>'日')）
	 */
	public function set_options($options)
	{
		//允许上传的文件格式
		//!$options['file_exts'] && $options['file_exts'] = 'rar zip 7z txt';
		$options['file_exts'] = $options['file_exts']?:'rar zip 7z txt';
		//允许上传的最大尺寸
		//!$options['file_upload_size'] && $options['file_upload_size'] = size_bytes(ini_get('upload_max_filesize'));
		$options['file_upload_size'] = $options['file_upload_size']?:size_bytes(ini_get('upload_max_filesize'));
		//文件夹存放形式
		//!$options['file_dir_mod'] && $options['file_dir_mod'] = 'MONTH';
		$options['file_dir_mod'] = $options['file_dir_mod']?:'MONTH';

		$this->option = ms_attr::create($options);

		$this->set_file_exts($options['file_exts']);
		$this->file_upload_size($options['file_upload_size']);
		$this->set_dir_mod($options['file_dir_mod']);
	}

	//设置允许上传的文件格式
	public function set_file_exts($exts) {
		if(!$exts) return '';
		$exts = explode(' ', strtolower($exts));
		foreach($exts as $k => $v) {
			if(!$v) {
				unset($exts[$k]);
			} elseif(preg_match("/(php|php3|inc|asp|jsp|aspx|shtml|vbs|do)/i",$v)) {
				unset($exts[$k]);
			} else {
				$exts[$k] = strtolower($v);
			}
		}
		if($exts) $this->option->file_exts = $exts;
	}

	//设置文件存放目录格式
	public function set_dir_mod($mod) {
		$this->option->file_dir_mod = $mod;
	}

	//设置最大上传文件大小
	public function file_upload_size($size) {
		$max_size = size_bytes(ini_get('upload_max_filesize'));
		$this->option->max_filesize = min($max_size, size_bytes($size.'k'));
	}

	/**
	 * 文件上传处理
	 * @param  string $root_dirname 上传文件的父级目录名（上传文件夹统一管理下的县一级目录）
	 * @return boolean是否上传处理成功
	 */
	function upload($root_dir_name)
	{
		//上传检测
		if($this->has_error() || !$this->check_file()) return false;

		//文件夹路径
		$this->path = ms_attachment::create_dir($root_dir_name, $this->option->file_dir_mod);
		if(!$this->path) return $this->add_error('文件夹无法创建，请检查系统读写权限。');

		//文件名
		$file_name = $this->create_file_name();

		//保存文件的具体路径(相对路径)
		$file_path = $this->path.DS.$file_name;

		//移动上传文件到最终保持路径
		if (move_uploaded_file($this->_file['tmp_name'], MUDDER_ROOT.$file_path)) {
			$this->filename = $file_name;	//文件名称
			$this->filepath = $file_path;	//完整的文件路径+文件名
			$this->url 		= str_replace(DS, '/', $file_path);	//URL相对路径
			$this->filesize = filesize(MUDDER_ROOT.$file_path);
			//删除临时文件
			$this->delete_tmpfile();
			return true;
		} else {
			return $this->add_error('global_upload_lost');
		}
	}

	//获取上传到临时文件的文件完整路径
	public function get_filename() {
		$filename = str_replace("\\\\", "\\", $this->_file['tmp_name']);
		return $filename;
	}

	//获取上传到临时文件夹的文件内容
	function get_contents() {
		$filename = $this->get_filename();
		return file_get_contents($filename);
	}

	//删除临时文件
	function delete_tmpfile() {
		return @unlink(str_replace("\\\\", "\\", $this->_file['tmp_name']));
	}

	//删除以保存的文件
	function delete_file() {
		if(is_file($this->full_filename)) {
			return @unlink($this->full_filename);
		}
		return false;
	}

	//检测上传文件
	protected function check_file()
	{
		if(!$this->key_name) {
			return $this->add_error('global_upload_empty_keyname');
		}

		//第一步检测
		if(empty($_FILES[$this->key_name])) {
			return $this->add_error('global_upload_error_4',4);
		}
		if($_FILES[$this->key_name]['error'] > 0) {
			return $this->add_error('global_upload_error_' . $_FILES[$this->key_name]['error'], $_FILES[$this->key_name]['error']);
		}

		$this->_file =& $_FILES[$this->key_name];
		//第二步检测
		if(!is_uploaded_file($this->_file['tmp_name'])) { //是否为上传文件
			return $this->add_error('global_upload_unkown');
		} elseif(!$this->check_file_ext($this->_file['name'])) {  //文件格式检测
			$this->delete_tmpfile();
			return $this->add_error(lang('global_upload_type_invalid', implode('，', $this->option->file_exts)));
	   // } if(filesize($this->_file['tmp_name']) != $this->_file['size']) {  //文件大小一致性检测
	   //   $this->delete_tmpfile();
	   // 	return $this->add_error('global_upload_size_invalid');
		} elseif($this->_file['size'] > $this->option->max_filesize) {  //文件大小是否超过最大限制
			$this->delete_tmpfile();
			return $this->add_error(lang('global_upload_szie_thraw', array(floor($this->option->max_filesize/1024),'KB')));
		}

		return true;
	}

	//判断上传文件格式是否可接受
	function check_file_ext()
	{
		$ext = strtolower(pathinfo($this->_file['name'], PATHINFO_EXTENSION));
		if(!$ext) return false;
		return in_array($ext, $this->option->file_exts);
	}

	//生成一个唯一文件名
	function create_file_name()
	{
		//获取上传文件信息
		$fileinfo = pathinfo($this->_file['name']);
		//文件后缀名
		$ext = strtolower($fileinfo["extension"]); 

		//检查配置参数里是否已经指定了文件的名称
		if(!$this->option->lock_name) {
			$rand = ($this->prefix?:'').mt_rand(1, 100);
			$name = $rand.'_'._G('timestamp').'.'.$ext;
		} else {
			$name = $this->option->lock_name . '.' . $ext;
		}
		return $name;
	}

}

/**
 * 上传图片文件
 */
class upload_image extends upload_file
{
	public $width;		//图片宽度
	public $height;		//图片高度
	public $type;		//图片格式标记 1,2.....
	public $attr; 		//图片属性 width=x height=y

	//缩略图
	protected $thumbs = array();
	
	public function __construct($key_name, $options)
	{
		parent::__construct($key_name, $options);
	}

	public function set_options($options)
	{
		!$options['file_exts'] && $options['file_exts'] = trim(S('picture_ext'));
		//允许上传的最大尺寸
		!$options['file_upload_size'] && $options['file_upload_size'] = (int)S('picture_upload_size');
		//文件夹存放形式
		!$options['file_dir_mod'] && $options['file_dir_mod'] = trim(S('picture_dir_mod'));

		//是否使用需要打打水印
		!isset($options['use_watermark']) && $options['use_watermark'] = (boolean)S('watermark');
		//是否自动虽小缩小超过最大尺寸的图片
		!isset($options['use_sizelimit']) && $options['use_sizelimit'] = true;

		parent::set_options($options);

		//图片最大尺寸限制，自动缩小
		if(!$options['picture_max_size']) {
			$options['picture_max_size']['width'] = (int)S('picture_max_width');
			$options['picture_max_size']['height'] = (int)S('picture_max_height');
		}
		!$options['picture_max_size']['width'] && $options['picture_max_size']['width'] = 800;
		!$options['picture_max_size']['height'] && $options['picture_max_size']['height'] = 600;
		$this->set_picture_max_size($options['picture_max_size']);

		//图片最小尺寸限制，小于设置则中断操作
		if(is_array($options['picture_limit_size'])) {
			$this->set_picture_limit_size($options['picture_limit_size']);
		}

		//图片处理参数
		//水印文字
		!$options['watermark_text'] && $options['watermark_text'] = (int)S('watermark_text');
		//缩略图质量等级
		!$options['picture_createthumb_level'] && $options['picture_createthumb_level'] = (int)S('picture_createthumb_level');
		//图片缩小模式
		!$options['picture_createthumb_mod'] && $options['picture_createthumb_mod'] = (int)S('picture_createthumb_mod');
		//原图添加水印的尺寸限制
		!$options['watermark_limitsize_width'] && $options['watermark_limitsize_width'] = (int)S('watermark_limitsize_width');
		!$options['watermark_limitsize_height'] && $options['watermark_limitsize_height'] = (int)S('watermark_limitsize_height');
		//水印位置
		!$options['watermark_postion'] && $options['watermark_postion'] = (int)S('watermark_postion');
		//图片处理类文件
		if(!$this->image_obj) {
			$this->image_obj = new ms_image($options);
		} else {
			$this->image_obj->set_options($options);
		}
	}

	//设置图片最大尺寸
	public function set_picture_max_size($picture_max_size)
	{
		$picture_max_size['width'] = (int)$picture_max_size['width'];
		$picture_max_size['height'] = (int)$picture_max_size['height'];
		$this->option->picture_max_size = $picture_max_size;
	}

	//设置图片最小上传尺寸
	public function set_picture_limit_size($picture_limit_size)
	{
		$picture_limit_size['width'] = (int)$picture_limit_size['width'];
		$picture_limit_size['height'] = (int)$picture_limit_size['height'];
		$this->option->picture_limit_size = $picture_limit_size;
	}

	//增加一张缩略图
	public function add_thumb($keyname, $prefix, $width, $height, $level = 0)
	{
		$this->thumbs[$keyname] = array(
			'prefix'	=> $prefix, 
			'width'		=> (int)$width, 
			'height'	=> (int)$height, 
			'level'		=> $level > 0 ?: $this->option->picture_createthumb_level,
		);
	}


	/**
	 * 上传图片处理
	 * @param  string  $root_dir_name	保存图片的父文件夹名
	 * @param  boolean $delete_sorcue	是否删除原图
	 * @return boolean
	 */
	function upload($root_dir_name, $delete_sorcue = FALSE) 
	{
		if(!parent::upload($root_dir_name)) return false;

		$sorcuefile = MUDDER_ROOT.$this->filepath;
		//判断图片是否有效
		if(function_exists('getimagesize') && !@getimagesize($sorcuefile)) {
			$this->delete_file();
			return $this->add_error('global_upload_image_unkown');
		}
		//获取图片信息（尺寸等属性）
		list($this->width, $this->height, $this->type, $this->attr) = @getimagesize($sorcuefile);
		if(!in_array($this->type, array(1, 2, 3))) {
			$this->delete_file();
			return $this->add_error('global_upload_image_type_support_invalid');
		}

		//如果上传的图片比指定要求的图片尺寸小，则中断
		if($this->option->picture_limit_size) {
			if($this->width < $this->option->picture_limit_size['width'] || $this->height < $this->option->picture_limit_size['height']) {
				return $this->add_error('global_upload_image_limit_size_invalid');
			}
		}

		//是否生成缩略图
		if($this->thumbs) {
			foreach($this->thumbs as $key => $val) {
				$savefile = MUDDER_ROOT.$this->path.DS.$val['prefix'].$this->filename;
				$this->image_obj->thumb($sorcuefile, $savefile, $val['width'], $val['height'], $val['level']);
				//缩略图变量，尺寸属性保存
				$this->thumb_filenames[$key]['filename'] = $val['prefix'].$this->filename;
				$this->thumb_filenames[$key]['filepath'] = $this->path.DS.$this->thumb_filenames[$key]['filename'];
				$this->thumb_filenames[$key]['url'] = str_replace(DS, '/', $this->thumb_filenames[$key]['filepath']);
				list($this->thumb_filenames[$key]['width'], $this->thumb_filenames[$key]['height'], 
					$this->thumb_filenames[$key]['type'], 
					$this->thumb_filenames[$key]['attr']) = @getimagesize($savefile);
			}
		}

		//图片自动缩小
		//auto_resize 函数内部会进行判断是否需要缩小
		//自动限制最大图片尺寸，防止用户传超大图片
		if($this->option->use_sizelimit && $this->option->picture_max_size) {
			//图片尺寸是否需要缩小判断
			$this->image_obj->auto_resize($sorcuefile, 
				$this->option->picture_max_size['width'],
				$this->option->picture_max_size['height']
			);
		}

		//原图加水印
		if($this->option->use_watermark && $this->image_obj->need_watermark($sorcuefile)) {
			$this->image_obj->watermark($sorcuefile, $sorcuefile);
		}

		//删除原图文件
		if($delete_sorcue) {
			$this->delete_file();
		}
		return true;
	}
}

/** end */